# Copyright (c) 2025 Li Auto Inc. and its affiliates
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
###############################################################################
# CMake build rules for EDDS
###############################################################################

# Set CMAKE_BUILD_TYPE to Release by default.
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
    message(STATUS "Setting build type to 'Release' as none was specified.")
    set(CMAKE_BUILD_TYPE Release CACHE STRING
        "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel."
        FORCE)
    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()

if (MINGW64)
    if(VBSAUTH)
        find_library(VBSAUTH_LIB
            NAMES vbsauth
            PATHS ${VBSAUTH_DIR}/lib
            REQUIRED)

        if(NOT VBSAUTH_LIB)
            message(FATAL_ERROR "vbsauth library not found!")
        else()
            message(STATUS "Found vbsauth library: ${VBSAUTH_LIB}")
        endif()
    endif()

    add_definitions(-DMINGW64)
    list(APPEND UNIFIED_DEFINES "-DMINGW64")
endif()

if (VBS_VERSION)
    add_definitions(-DVBS_VERSION="${VBS_VERSION}")
    list(APPEND UNIFIED_DEFINES "-DVBS_VERSION=\"${VBS_VERSION}\"")
endif()

if (ANDROID)
    set(COMPILE_EXAMPLES OFF)
    set(COMPILE_TEST OFF)
    add_compile_definitions(ANDROID_NDK_BUILD)
    list(APPEND UNIFIED_DEFINES "-DANDROID_NDK_BUILD")
endif()

###############################################################################
# Project
###############################################################################
if (DEFINED ENV{VBS_VERSION})
    # 获取环境变量 VERSION 的值
    set(VERSION $ENV{VBS_VERSION})

    # 使用正则表达式提取主版本号
    string(REGEX MATCH "^[0-9]+\\.[0-9]+\\.[0-9]+" MAIN_VERSION ${VERSION})
    message("get MAIN_VERSION from env VBS_VERSION is ${MAIN_VERSION}")

    # 输出结果
    if (MAIN_VERSION)
        message("env get MAIN_VERSION is ${MAIN_VERSION}")
        project(ertps VERSION ${MAIN_VERSION} LANGUAGES C CXX)
    else()
        message("MAIN_VERSION does not match the regex pattern")
        project(ertps VERSION 1.1.0 LANGUAGES C CXX)
    endif()
else()
    project(ertps VERSION 1.1.0 LANGUAGES C CXX)
endif()

set(PROJECT_NAME_LARGE "ERTPS")
string(TOUPPER "${PROJECT_NAME}" PROJECT_NAME_UPPER)

option(CONAN_BUILD "Conan build option" OFF)
option(CONAN_INSTALL "Conan build option" OFF)
option(COMPILE_COVB "Statistical code coverage" OFF)

message(STATUS "Version: ${PROJECT_VERSION}")

###############################################################################
# vbs build options
###############################################################################
option(VBS_BUILD "Activate internal building" OFF)

if (CONAN_BUILD)
    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -std=gnu++17 -fPIC -MD -MT  \
                                            -Wall -Wextra -Wpedantic -Werror\
                                            -fdiagnostics-color=always")# -Werror=non-virtual-dtor
    if(CONAN_PLATFORM STREQUAL "orin")
        SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv8.2-a")
    endif()
endif()

###############################################################################
# Warning level
###############################################################################
if(MSVC OR MSVC_IDE)
    if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
        string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
    else()
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
    endif()

    if(VBS_EXTRA_CMAKE_CXX_FLAGS)
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${VBS_EXTRA_CMAKE_CXX_FLAGS}")
    endif()

    add_compile_options(/w34101 /w34189 /w34555 /w34715 /w35038 /w44100)

    if(VBS_BUILD)
        string(REPLACE "/DNDEBUG" "" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
    endif()
else()
    set(CMAKE_CXX_FLAGS
        "${CMAKE_CXX_FLAGS} -Wall -pedantic -Wextra -Wno-unknown-pragmas -Wno-error=deprecated-declarations")
    if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-psabi")
        set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined")
    elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
        set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-undefined,error")
    endif()

    if(VBS_BUILD)
        string(REPLACE "-DNDEBUG" "" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
    endif()
endif()

###############################################################################
# GCC colors if using CCache
###############################################################################
if("${CMAKE_CXX_COMPILER_LAUNCHER}" STREQUAL "ccache" AND
        CMAKE_COMPILER_IS_GNUCXX AND
        CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4,8)
    add_compile_options(-fdiagnostics-color=always)
endif()

###############################################################################
# Test system configuration
###############################################################################
include(${PROJECT_SOURCE_DIR}/cmake/common/check_configuration.cmake)

set(FORCE_CXX "17" CACHE STRING "C++ standard fulfillment selection")
check_stdcxx(${FORCE_CXX})
check_endianness()

###############################################################################
# Installation paths
###############################################################################
option(APPEND_PROJECT_NAME_TO_INCLUDEDIR
  "When ON headers are installed to a path ending with a folder called \
  ${PROJECT_NAME}. This avoids include directory search order issues when \
  overriding this package from a merged catkin, ament, or colcon workspace."
  OFF)

set(BIN_INSTALL_DIR bin/ CACHE PATH "Installation directory for binaries")
set(_include_dir "include/")
if(APPEND_PROJECT_NAME_TO_INCLUDEDIR)
  string(APPEND _include_dir "${PROJECT_NAME}/")
endif()
set(INCLUDE_INSTALL_DIR "${_include_dir}" CACHE PATH "Installation directory for C++ headers")
unset(_include_dir)

set(LIB_INSTALL_DIR lib/ CACHE PATH "Installation directory for libraries")
set(DATA_INSTALL_DIR share/ CACHE PATH "Installation directory for data")

if(WIN32)
    set(DOC_DIR "doc")
else()
    set(DOC_DIR "${DATA_INSTALL_DIR}/doc")
endif()

set(DOC_INSTALL_DIR ${DOC_DIR} CACHE PATH "Installation directory for documentation")
set(LICENSE_INSTALL_DIR ${DATA_INSTALL_DIR}/${PROJECT_NAME} CACHE PATH "Installation directory for licenses")

if(NVBSUTILS)
    add_definitions(-DNVBSUTILS)
    list(APPEND UNIFIED_DEFINES "-DNVBSUTILS")
endif()

add_definitions(-DNDEBUG)
list(APPEND UNIFIED_DEFINES "-DNDEBUG")
if (CONAN_BUILD)
    include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
    conan_basic_setup()
    set_property(DIRECTORY PROPERTY INCLUDE_DIRECTORIES "")
    set(TINYXML2_LIBRARY ${CONAN_LIBS_TINYXML2})
    set(TINYXML2_INCLUDE_DIR ${CONAN_INCLUDE_DIRS_TINYXML2})
    set(CONAN_INSTALL ON)
    if(CMAKE_BUILD_TYPE STREQUAL "Release")
        set(CMAKE_CXX_FLAGS_RELEASE "-O3")
        set(CMAKE_C_FLAGS_RELEASE "-O3")
    elseif(CMAKE_BUILD_TYPE STREQUAL "Debug")
        set(CMAKE_CXX_FLAGS_DEBUG "-g -O3")
        set(CMAKE_C_FLAGS_DEBUG "-g -O3")
    endif()
    set(VBS_VERSION ${CONAN_PACKAGE_VERSION})
    message("VBS_VERSION is:  ${VBS_VERSION}")
    add_definitions(-DVBS_VERSION="${VBS_VERSION}")
    list(APPEND UNIFIED_DEFINES "-DVBS_VERSION=\"${VBS_VERSION}\"")
endif()

if (COMPILE_COVB)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage")
    message(STATUS "CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}")
endif()

###############################################################################
# Internal debug messages
###############################################################################
if(VBS_BUILD)
    set(INTERNAL_DEBUG ON)
endif()

###############################################################################
# Load CMake modules
###############################################################################
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/cmake/modules)

###############################################################################
# Check libraries
###############################################################################
set(Boost_USE_STATIC_LIBS OFF)
set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_RUNTIME OFF)
if (ANDROID)
    set(Boost_USE_STATIC_LIBS ON)
    set(Boost_USE_STATIC_RUNTIME ON)
endif()

###############################################################################
# Default shared libraries
###############################################################################
# Global flag to cause add_library() to create shared libraries if on.
# If set to true, this will cause all libraries to be built shared
# unless the library was explicitly added as a static library.
option(BUILD_SHARED_LIBS "Create shared libraries by default" ON)

###############################################################################
# Load external projects.
###############################################################################
include(${PROJECT_SOURCE_DIR}/cmake/common/vbs_libraries.cmake)

# if we are building EDDS as a static library we must load ECDR as one
if(NOT BUILD_SHARED_LIBS)
    set(EDDS_STATIC ON)
endif()

if (CONAN_BUILD)
    vbs_find_thirdparty(asio REQUIRED)
    vbs_find_thirdparty(tinyxml2 REQUIRED)
    vbs_find_thirdparty(GTest REQUIRED)
    if (NOT MINGW64)
        set(ecdr ecdr)
    endif()
endif()

find_package(ThirdpartyBoost REQUIRED)
if(ANDROID)
    if((ANDROID_PLATFORM LESS_EQUAL 23) OR (ANDROID_NATIVE_API_LEVEL LESS_EQUAL 23))
        vbs_find_thirdparty(android-ifaddrs android-ifaddrs)
    endif()
endif()

if (CONAN_BUILD)
    include_directories(${CONAN_INCLUDE_DIRS_NLOHMANN_JSON})
    include_directories(${CONAN_INCLUDE_DIRS_OPTIONPARSER})
else()
    include_directories(../thirdparty/nlohmann-json)
endif()
include_directories(../thirdparty/filewatch)

###############################################################################
# complie SHM transport
###############################################################################
option(SHM_TRANSPORT_COMPILE "Add SHM transport to the default transports" OFF)

###############################################################################
# complie TCP transport
###############################################################################
option(TCP_TRANSPORT_COMPILE "Add TCP transport to the default transports" OFF)

###############################################################################
# complie IPC transport
###############################################################################
option(IPC_TRANSPORT_COMPILE "Add IPC transport to the default transports" OFF)

###############################################################################
# LogConsumer default setup
###############################################################################
set(LOG_CONSUMER_DEFAULT AUTO CACHE STRING "Selects default LogConsumer")
set_property(CACHE LOG_CONSUMER_DEFAULT PROPERTY STRINGS AUTO STDOUT STDOUTERR)

if(LOG_CONSUMER_DEFAULT STREQUAL "STDOUTERR")
    set(STDOUTERR_LOG_CONSUMER ON)
endif()

###############################################################################
# Log Info default setup
###############################################################################
include(CMakeDependentOption)

set(LOG_NO_INFO_HELP "\
Do not compile Info Log level. For the sake of efficiency non-debug \
configs do not log info messages unless the option EDDS_ENFORCE_LOG_INFO \
is set to on.")
if(CMAKE_BUILD_TYPE MATCHES "^([Dd][Ee][Bb][Uu][Gg])$"  # single config generator
   OR ("Debug" IN_LIST CMAKE_CONFIGURATION_TYPES))      # multi config generator
    option(LOG_NO_INFO ${LOG_NO_INFO_HELP} OFF)
else()
    option(LOG_NO_INFO ${LOG_NO_INFO_HELP} ON)
endif()
unset(LOG_NO_INFO_HELP)

option(LOG_NO_WARNING "Do not compile Warning Log level" OFF)
option(LOG_NO_ERROR "Do not compile Error Log level" OFF)

cmake_dependent_option(
    EDDS_ENFORCE_LOG_INFO
    "The LOG_NO_INFO option must be enforced regardless of selected configuration"
    OFF
    "NOT LOG_NO_INFO"
    OFF)

if(LOG_NO_INFO)
    set(HAVE_LOG_NO_INFO 1)
else()
    set(HAVE_LOG_NO_INFO 0)
endif()

if(LOG_NO_WARNING)
    set(HAVE_LOG_NO_WARNING 1)
else()
    set(HAVE_LOG_NO_WARNING 0)
endif()

if(LOG_NO_ERROR)
    set(HAVE_LOG_NO_ERROR 1)
else()
    set(HAVE_LOG_NO_ERROR 0)
endif()

###############################################################################
# enable Intraprocess
###############################################################################
option(INTRA_PROCESS_ENABLE "Enable Intraprocess" OFF)
message(STATUS "Intraprocess Module is ${INTRA_PROCESS_ENABLE}")
if(INTRA_PROCESS_ENABLE)
  add_definitions(-DINTRA_PROCESS_ENABLE=1)
  list(APPEND UNIFIED_DEFINES "-DINTRA_PROCESS_ENABLE=1")
endif()

###############################################################################
# enable Batch send
###############################################################################
option(BATCH_SEND_ENABLE "Enable Batch send" OFF)
message(STATUS "Batch send is ${BATCH_SEND_ENABLE}")
if(BATCH_SEND_ENABLE)
  add_definitions(-DBATCH_SEND_ENABLE=1)
  list(APPEND UNIFIED_DEFINES "-DBATCH_SEND_ENABLE=1")
endif()

###############################################################################
# EDDS statistics tool default setup
###############################################################################
option(EDDS_METRICS "Enable EDDS Metrics Module" ON)
if(EDDS_METRICS)
    add_definitions(-DEDDS_METRICS)
    list(APPEND UNIFIED_DEFINES "-DEDDS_METRICS")
endif()

###############################################################################
# Add http://optionparser.sourceforge.net/ as unified cli parser
###############################################################################
if(BUILD_UNIFIED_LIB)
    if (CONAN_BUILD)
        list(APPEND UNIFIED_INCLUDE_DIRS ${CONAN_INCLUDE_DIRS_NLOHMANN_JSON} ${CONAN_INCLUDE_DIRS_OPTIONPARSER})
    else()
        list(APPEND UNIFIED_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/nlohmann-json)
    endif()
    list(APPEND UNIFIED_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/filewatch)
    list(APPEND UNIFIED_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/folly)

    set(UNIFIED_INCLUDE_DIRS ${UNIFIED_INCLUDE_DIRS} CACHE INTERNAL "Unified include directories for all libraries")
    set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} CACHE INTERNAL "Unified cxx flags for all libraries")
    set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} CACHE INTERNAL "Unified c flags for all libraries")
    set(UNIFIED_DEFINES ${UNIFIED_DEFINES} CACHE INTERNAL "Unified defines for all libraries")
    set(UNIFIED_SOURCES ${UNIFIED_SOURCES} CACHE INTERNAL "Unified sources for all libraries")
    # 添加子目录,但不实际构建库
    add_subdirectory(src/cpp)
endif()

###############################################################################
# Fuzzers
###############################################################################
if(DEFINED ENV{LIB_FUZZING_ENGINE})
    add_subdirectory(fuzz)
endif()

###############################################################################
# Documentation
###############################################################################
# Add an option to toggle the generation of the API documentation.
option(BUILD_DOCUMENTATION "Use doxygen to create product documentation" OFF)
option(CHECK_DOCUMENTATION "Use doxygen to check code documentation" OFF)

if(CHECK_DOCUMENTATION)
    set(BUILD_DOCUMENTATION ON)
endif()

if(BUILD_DOCUMENTATION)
    find_package(Doxygen)
    if(NOT DOXYGEN_FOUND)
        message(FATAL_ERROR "doxygen is needed to build the documentation. Please install it correctly")
    endif()
    if(UNIX)
        find_program(DOXYFILE_MAKE make)
        if(DOXYFILE_MAKE)
            message(STATUS "Found Make: ${DOXYFILE_MAKE}")
        else()
            message(FATAL_ERROR "make is needed to build the documentation. Please install it correctly")
        endif()
    elseif(WIN32)
        set(DOXYFILE_MAKE make.bat)
    endif()

    if(NOT CHECK_DOCUMENTATION)
        find_program(UNZIP_EXE unzip)
        if(UNZIP_EXE)
            message(STATUS "Found Unzip: ${UNZIP_EXE}")
        else()
            message(FATAL_ERROR "unzip is needed to build the documentation. Please install it correctly")
        endif()
    endif()

    # Target to create documentation directories
    add_custom_target(docdirs
        COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/doc
        COMMENT "Creating documentation directory" VERBATIM)

    ### Doxygen ########################3
    if(CHECK_DOCUMENTATION)
        set(USE_DOT NO)
    else()
        set(USE_DOT YES)
    endif()
    # Configure the template doxyfile for or specific project
    configure_file(doxyfile.in ${PROJECT_BINARY_DIR}/doxyfile @ONLY IMMEDIATE)
    # Add custom target to run doxygen when ever the project is build
    add_custom_target(doxygen
        COMMAND "${DOXYGEN_EXECUTABLE}" "${PROJECT_BINARY_DIR}/doxyfile"
        SOURCES "${PROJECT_BINARY_DIR}/doxyfile"
        COMMENT "Generating API documentation with doxygen" VERBATIM)

    add_dependencies(doxygen docdirs)

    ### README html ########################

    if(WIN32)
        set(README_LOCATION "${PROJECT_BINARY_DIR}/")
        set(README_LOCATION_PREFFIX "doc/")
        set(README_INSTALL_LOCATION ".")
    else()
        set(README_LOCATION "${PROJECT_BINARY_DIR}/doc/")
        set(README_INSTALL_LOCATION "${DOC_INSTALL_DIR}")
    endif()

    configure_file(doc/README.html.in ${README_LOCATION}/README.html @ONLY IMMEDIATE)

    ### ReadTheDocs ########################
    if(NOT CHECK_DOCUMENTATION)
        file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/readthedocs_custom_template.cmake [=[
            file(DOWNLOAD "https://edds.docs.lixiang.com/_/downloads/en/v${PROJECT_VERSION}/htmlzip/" "./evbs-rtps.zip")
            # TODO: when windows ci CMake version surpasses 17 favor file() instead of UNZIP as in the next line
            # file(ARCHIVE_EXTRACT INPUT "./evbs-rtps.zip" DESTINATION  "${PROJECT_BINARY_DIR}/doc/")
            execute_process(COMMAND "${UNZIP_EXE}" "./evbs-rtps.zip" -d "${PROJECT_BINARY_DIR}/doc/")
            file(REMOVE_RECURSE "${PROJECT_BINARY_DIR}/doc/manual")
            file(RENAME "${PROJECT_BINARY_DIR}/doc/evbs-rtps-v${PROJECT_VERSION}" "${PROJECT_BINARY_DIR}/doc/manual")
            file(REMOVE  "./evbs-rtps.zip")
            ]=])

        configure_file(${CMAKE_CURRENT_BINARY_DIR}/readthedocs_custom_template.cmake  ${CMAKE_CURRENT_BINARY_DIR}/readthedocs_custom.cmake)

        add_custom_target(readthedocs
            COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/readthedocs_custom.cmake
            )

        add_dependencies(readthedocs docdirs)
    endif()

    add_custom_target(doc ALL
        COMMENT "Generated project documentation" VERBATIM)

    add_dependencies(doc doxygen)
    if(NOT CHECK_DOCUMENTATION)
        add_dependencies(doc readthedocs)
    endif()
endif()

###############################################################################
# Packaging
###############################################################################
# Install licenses
if(NOT CONAN_INSTALL)
    install(FILES ${PROJECT_SOURCE_DIR}/LICENSE
        DESTINATION ${LICENSE_INSTALL_DIR}
        COMPONENT licenses
        )
endif()

if(BUILD_DOCUMENTATION)
    # Installation of doxygen files
    install(DIRECTORY ${PROJECT_BINARY_DIR}/doc/api_reference
        DESTINATION ${DOC_INSTALL_DIR}
        COMPONENT documentation
        )

    install(FILES "${README_LOCATION}/README.html"
        DESTINATION ${README_INSTALL_LOCATION}
        COMPONENT documentation
        )

    install(FILES "${README_LOCATION}/RPC_README.html"
        DESTINATION ${README_INSTALL_LOCATION}
        COMPONENT documentation
        )

    install(FILES "${LIBREOFFICE_CONVERT_USER_MANUAL_DOC}.pdf"
        DESTINATION ${DOC_INSTALL_DIR}/pdf
        RENAME "${VBSRPC_LARGE} - User Manual.pdf"
        COMPONENT documentation
        )

    if(NOT CHECK_DOCUMENTATION)
        install(DIRECTORY ${PROJECT_BINARY_DIR}/doc/manual
            DESTINATION ${DOC_INSTALL_DIR}
            COMPONENT documentation
            )
    endif()
endif()

file(GLOB SCRIPTS_TO_COPY "${PROJECT_SOURCE_DIR}/thirdparty/*.sh")
file(COPY ${PROJECT_SOURCE_DIR}/../thirdparty/ECDR
    ${SCRIPTS_TO_COPY}
    DESTINATION thirdparty
    )

file(COPY
    ${PROJECT_SOURCE_DIR}/../thirdparty/ECDR
    DESTINATION ../thirdparty
    )

install(DIRECTORY ${PROJECT_BINARY_DIR}/../thirdparty/ECDR/include
    DESTINATION include/ecdr)

if (COMPILE_EXAMPLES OR COMPILE_TEST)
    install(FILES ${SCRIPTS_TO_COPY}
        DESTINATION test/evbs/thirdparty
        PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ
        GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ)
endif()
